package ChessEngine;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Queue;

import javax.swing.text.AbstractDocument.BranchElement;

import ChessEngine.moveGenerator.MoveGenerator;
import boardData.*;

/**
 * * Spielbaum, der waehren der Suche aufgebaut wird. Der Spielbaum kapselt die
 * einzelnen Stellungen in der Klasse <code>Node</code>
 * 
 * @author daniel, nico
 * 
 */
public class GameTree {
	private Node root;

	public GameTree() {

	}

	/**
	 * Erstelle ein neues <code>GameTree</code> Objekt
	 * 
	 * @param br
	 *            Boardpraesentation
	 * @param isWhiteMove
	 *            Farbe (die am zug ist) des Root.Knoten.
	 */
	public GameTree(Board br, boolean isWhiteMove) {
		/*
		 * Erster Knoten (Wurzel) hat genau ein Element in der Liste, eben die
		 * Startposition
		 */
		// Dieser Knoten ist die Wurzel des Spielbaumes
		root = new Node(br, null, isWhiteMove);
	}

	/**
	 * 
	 * @return Die Wurzel des Spielbaums, das ist die Stellung für die der beste
	 *         Nachfolgezug gefunden werden soll
	 */
	public Node getRoot() {
		return this.root;
	}

	/**
	 * 
	 * @param b
	 *            die neue Stellung des Boards
	 * @param isWhiteMove
	 *            Die farbe die am zug ist
	 */
	public void setRootNode(Board b, boolean isWhiteMove) {
		Node newRootNode = new Node(b, null, isWhiteMove);
		this.root = newRootNode;
	}

}

/**
 * Kapselt eine Stellung (Board Objekt) um den Suchbaum aufzubauen
 * @author daniel, nico
 * 
 */
class Node {
	/**
	 * Liste mit moeglichen Nachfolgestellungen
	 */
	private LinkedList<Node> childNodes;
	/**
	 * Die Datenstruktur der Stellung an sich. enthaelt alle notwendigen
	 * informationen zur Stellung
	 */
	private Board boardData; // aktuelle Boardrepraesentation

	/**
	 * Farbe (true fuer weiss, false fuer schwarz) die in der Stellung am zug
	 * ist
	 */
	private boolean whiteMove;

	/**
	 * Bewertung der Stellung die dieser Knoten repraesentiert
	 */
	private int nodeEvaluation = 0;

	/**
	 * Variable die speichert, ob dieser Knoten in der Suche zu einem cutt
	 * fuehrt
	 */
	public boolean cutOff = false;

	/**
	 * Konstruktor erzeugt ein neuer Knoten ruft den ersten Konstruktor auf
	 * 
	 * @param br
	 *            aktuelle Boardpr�sentation
	 * @param father
	 *            Vater des aktuellen Knotens
	 */
	public Node(Board br, Node father, boolean isWhiteMove) {
		boardData = br;
		this.whiteMove = br.isSideWhite();
	}

	/**
	 * Liefert die Farbe (true fuer weiss, false fuer schwarz) die in der
	 * Stellung am zug ist zurueck
	 * 
	 * @return true, falls die weiß am Zug ist, false sonst
	 */
	public boolean isWhiteMove() {
		return whiteMove;
	}

	/**
	 * Liefert die zu der Boardpraesentation zugehoerigen Folgezuege in einer
	 * Liste zurueck
	 * 
	 * @return Folgezuege in einer LinkedList
	 */
	public LinkedList<Node> getList() {
		return this.childNodes;
	}

	/**
	 * Setzt die zu der Boardprepraesentation zugehoerigen Folgezuege
	 * 
	 * @param LinkedList
	 *            Folgezuege welche vom Zuggenerator generiert worden sind
	 */
	public void setList(LinkedList<Board> boardPlayerChoiceList) {
		LinkedList<Node> tmpList = new LinkedList<Node>();
		for (int i = 0; i < boardPlayerChoiceList.size(); i++) {
			Node tmpNode = new Node(boardPlayerChoiceList.get(i), this,
					!this.isWhiteMove());
			tmpList.add(tmpNode);
		}
		this.childNodes = tmpList;
	}

	/**
	 * Liefert die Stellung die der Knoten representiert zurueck
	 */
	public Board getBoardData() {
		return this.boardData;
	}

	/**
	 * Setzt die fuer die Stellung errechnete Bewertung
	 * 
	 * @param evaluation
	 *            die Bewertung der Stellung
	 */
	public void setEvaluation(int evaluation) {
		this.nodeEvaluation = evaluation;
	}

	/**
	 * 
	 * @return die Bewertung der Stellung die der Knoten repaesentiert
	 */
	public int getEvaluation() {
		return nodeEvaluation;
	}

	/**
	 * Überprueft ob zu diesem Knoten bereits Nachfolgestellungen abgespeichert
	 * wurden
	 * 
	 * @return true, falls der Knoten Nachfolgestellungen beinhaltet, false
	 *         sonst
	 */
	public boolean hasChildren() {

		if (this.childNodes == null || this.childNodes.size() == 0) {
			return false;
		}
		return true;
	}

	/**
	 * Methode zum formatierten, eingerueckten Ausgeben eines Knoten. Eingefuert
	 * zum testen und nachvollziehen des Suchalgorithmus
	 * 
	 * @param indent
	 */
	public void print(String indent) {
		if (this.isWhiteMove()) {

			System.out.println(indent + "W:" + this.getEvaluation()
					+ ((cutOff) ? "cut" : ""));
		} else {
			System.out.println(indent + "B" + this.getEvaluation()
					+ ((cutOff) ? "cut" : ""));
		}

		indent = indent + "\t";
		if (this.hasChildren()) {
			for (Node child : this.childNodes) {
				child.print(indent);
			}
		}
		return;
	}

	/**
	 * Druckt die wesentlichen informationen eines Knoten aus. Eingeführt um die
	 * Suche nachzuvollziehen.
	 * 
	 * @param indent
	 * @param out
	 * @throws IOException
	 */
	public void print(String indent, BufferedWriter out) throws IOException {

		if (this.isWhiteMove()) {

			out.write(indent + "W" + this.getEvaluation()
					+ ((cutOff) ? "cut" : "") + "\n");
		} else {
			out.write(indent + "B" + this.getEvaluation()
					+ ((cutOff) ? "cut" : "") + "\n");
		}
		indent = indent + "\t";
		if (this.hasChildren()) {
			for (Node child : this.childNodes) {

				if (child.getEvaluation() == 0) {
					out.write(indent + "cut\n");
					// continue;
				}
				child.print(indent, out);
			}
		}
		return;
	}

	/**
	 * setzt die möglichen Nachfolgestellungen
	 * 
	 * @param nodeList
	 *            Liste mit Nachfolgestellungen
	 */
	public void setNodeList(LinkedList<Node> nodeList) {
		this.childNodes = nodeList;
	}
}
